﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Heaven.Engine
{
    /// <summary>
    /// Represents a weak multicast delegate. 
    /// This class helps to make weak events
    /// </summary>
    public class WeakMulticastDelegate
    {
        #region Fields

        // Weak references to delegates
        List<WeakReference> weakTargetReferences = new List<WeakReference>(1);
        List<MethodInfo> weakMethodReferences = new List<MethodInfo>(1);

        #endregion

        #region Add & Remove

        /// <summary>
        /// Adds a delegate to the invocation list
        /// </summary>
        /// <param name="handler">The delegate</param>
        public void Add(Delegate value)
        {
            weakTargetReferences.Add(new WeakReference(value.Target));
            weakMethodReferences.Add(value.Method);
        }

        /// <summary>
        /// Removes a delegate to the invocation list
        /// </summary>
        /// <param name="value">The delegate</param>
        public void Remove(Delegate value)
        {
            for (int i = weakTargetReferences.Count - 1; i >= 0; i--)
            {
                object target = (object)weakTargetReferences[i].Target;
                if (target == null)
                {
                    weakTargetReferences.RemoveAt(i);
                    weakMethodReferences.RemoveAt(i);
                }
                else if ((target == value.Target) && (value.Method == weakMethodReferences[i]))
                {
                    weakTargetReferences.RemoveAt(i);
                    weakMethodReferences.RemoveAt(i);
                    return;
                }
            }
        }

        /// <summary>
        /// Clears the invocation list
        /// </summary>
        public void Clear()
        {
            weakTargetReferences.Clear();
            weakMethodReferences.Clear();
        }

        #endregion

        #region Invocations

        /// <summary>
        /// Raises the event
        /// </summary>
        /// <param name="sender">The source of the event</param>
        /// <param name="args">The event data</param>
        public void Invoke(object sender, EventArgs args)
        {
            for (int i = weakTargetReferences.Count - 1; i >= 0; i--)
            {
                object target = weakTargetReferences[i].Target;
                if (target == null)
                {
                    weakTargetReferences.RemoveAt(i);
                    weakMethodReferences.RemoveAt(i);
                }
                else weakMethodReferences[i].Invoke(target, new object[] { sender, args });
            }
        }

        /// <summary>
        /// Raises the event
        /// </summary>
        /// <param name="args">The event parameters</param>
        public void Invoke(params object[] args)
        {
            for (int i = weakTargetReferences.Count - 1; i >= 0; i--)
            {
                object target = weakTargetReferences[i].Target;
                if (target == null)
                {
                    weakTargetReferences.RemoveAt(i);
                    weakMethodReferences.RemoveAt(i);
                }
                else weakMethodReferences[i].Invoke(target, args);
            }
        }

        #endregion
    }
}
